//
//  HOMUtilities.h
//  HigherOrderMessaging
//
//  Created by Ofri Wolfus on 23/12/05.
//  Copyright 2005 Ofri Wolfus. All rights reserved.
//
//  This file is released under modified BSD license.
//

#import <Foundation/Foundation.h>
#include <objc/objc-class.h>
#include <AvailabilityMacros.h>
#import <HigherOrderMessaging/DPObjCRuntime.h>


/*
 * A macro for class clusters that needs to implement a method that must be overridden by a concrete subclass.
 * This macro must be used within the context of a method. It raises an NSInternalInconsistencyException exception when the method is being called.
 */
#if !defined (DP_SUBCLASS_MUST_IMPLEMENT)
#define DP_SUBCLASS_MUST_IMPLEMENT \
do {\
	[[NSException exceptionWithName:NSInternalInconsistencyException\
							 reason:[NSString stringWithFormat:@"%@'s implementation of %@ was not overridden!",\
								 class_getName(object_isInstance(self) ? (Class)(self->isa) : (Class)self), NSStringFromSelector(_cmd)]\
						   userInfo:nil] raise];\
} while (0)
// An alternative naming to DP_SUBCLASS_MUST_IMPLEMENT
#if !defined (DPSubclassMustOverride)
#define DPSubclassMustOverride DP_SUBCLASS_MUST_IMPLEMENT
#endif
#endif

@class NSAssertionHandler;

/* Asserts for Objective-C that accepts unlimited number of arguments */
#if !defined(NS_BLOCK_ASSERTIONS) && !defined(DPAssert)
/*
 * The usage of this macro is the same as with NSAssert, exept you don't need DPAssert1, DPAssert2, etc.
 * This macro accepts any number of arguments passed to the description.
 */
#define DPAssert(condition, desc...)\
do {\
	if (!(condition)) {\
		[[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd\
															object:self\
															  file:[NSString stringWithCString:__FILE__] \
														lineNumber:__LINE__\
													   description:desc];\
	}\
} while(0)
#endif

#if !__OBJC2__

/*
 Code taken from CocoaDev. See http://www.cocoadev.com/index.pl?ForallMacro for more details.
 Modified by Ofri Wolfus.
 */
#define _hom_foreach(element, collection...)  for(id __foreach_macro_element = nil, __foreach_macro_enumerator = [collection objectEnumerator]; \
								   __foreach_macro_element = !__foreach_macro_element ? [__foreach_macro_enumerator nextObject] : nil;) \
for (element = __foreach_macro_element; __foreach_macro_element; __foreach_macro_element = nil)

#else

// For ObjC 2 we just use the built in for (in) syntax
#define _hom_foreach(obj, collection...) for(obj in collection)

#endif

/*
 * These two macros enable a foreach (obj In collection) syntax for both
 * ObjC 1 and 2. The keyword "In" can optionally be replaced by a comma.
 * In ObjC 1, foreach() sends a -objectEnumerator message to the collection
 * and uses the returned NSEnumerator to achieve it's goal.
 * In ObjC 2, foreach() is translated to the built in for(in) syntax in order
 * to provide better performances.
 */
#define In ,
#define foreach(X...) _hom_foreach(X)


// A nice name for the always_inline attribute, in the style of the attributes macros from AvailabilityMacros.h
#if !defined (ALWAYS_INLINE_ATTRIBUTE)
#define ALWAYS_INLINE_ATTRIBUTE __attribute__((always_inline))
#endif


// End of supported section
#pragma mark -

/*
 * The next section has some stuff that some may find useful, but others will consider as hacks.
 * Be VERY careful when you use stuff from here, and make sure you know what you're doing!
 * Also remember that these stuff may stop working at *ANY* later stage or even completely disappear.
 *
 * With that being said, most people will find the DPSubclassMustOverride and DPAssert() macros useful.
 * Other stuff are useful for people that need close interaction with the Objective-C runtime (which most people don't need).
 */

/*
 * The following macros are deprecated.
 * Use the dp_ ones from DPObjCRuntime.h instead.
 */
// Returns whether the passed object is an instance
#define HOMObjectIsInstance(obj)	object_isInstance(obj)

//Checks if obj is *really* kind of cls.
//This is useful if you *need* to know whether a proxy is really a proxy since
//it may override -isKindOfClass to its target object.
#define hom_isKindOfClass(obj, cls) object_isKindOfClass(obj, cls)


//Renames originalSelector instance method of inClass to newSelector.
DP_EXTERN BOOL hom_renameInstnaceMethod(Class inClass, SEL originalSelector, SEL newSelector)	DEPRECATED_ATTRIBUTE;

//Renames originalSelector class method of inClass to newSelector.
DP_EXTERN BOOL hom_renameClassMethod(Class inClass, SEL originalSelector, SEL newSelector)	DEPRECATED_ATTRIBUTE;

//Rplaces the IMP of originalSelector instance method of classOne with the imp of newSelector of classTwo.
//Returns the original IMP of originalSelector.
DP_EXTERN IMP hom_replaceInstanceIMP(Class classOne, SEL originalSelector, Class classTwo, SEL newSelector)	DEPRECATED_ATTRIBUTE;

//Rplaces the IMP of originalSelector class method of classOne with the imp of newSelector of classTwo.
//Returns the original IMP of originalSelector.
DP_EXTERN IMP hom_replaceClassIMP(Class classOne, SEL originalSelector, Class classTwo, SEL newSelector)	DEPRECATED_ATTRIBUTE;


// Returns the number of arguments the passed selector accepts
#define hom_getNumberOfArguments(sel) sel_getNumberOfArguments(sel)

// Returnes the type of the argument at a given index.
// If index is 0 based, and if it exceeds the number of arguments, the result is undefined.
DP_EXTERN const char * hom_getArgumentTypeAtIndex(Method m, unsigned index);

DP_EXTERN const char * hom_getArgumentFromTypes(const char *types, unsigned index);

// Returnes the type of the argument at a given index.
// If index is 0 based, and if it exceeds the number of arguments, the result is undefined.
// You are responsible for freeing the returned string.
DP_EXTERN char *hom_copyArgumentTypeAtIndex(Method m, unsigned index);

// Returnes the offset of the argument at a given index.
// If index is 0 based, and if it exceeds the number of arguments, the result is undefined.
// Use hom_sizeOfType() if you need to get the size of the argument.
DP_EXTERN int hom_getArgumentOffsetAtIndex(Method m, unsigned index);

DP_EXTERN int hom_getArgumentOffsetFromTypes(const char *types, unsigned index);
